<?php
/**
 * QQ 授权登录
 * Created by PhpStorm.
 * User: 我想有个家
 * Date: 2019/6/14
 * Time: 11:04
 */

namespace App\Service;


use App\Log;
use Hyperf\Contract\ConfigInterface;

class QQAuthLogin {
    private $appid;
    private $appkey;
    protected $callbackUrl;

    public function __construct(ConfigInterface $config) {
        $qq = $config->get('md.qq');
        $this->appid = $qq['appid'];
        $this->appkey = $qq['appkey'];
        $this->callbackUrl = urlencode($qq['callbackUrl']);
    }

    /**
     * 获取跳转地址
     * @return string
     */
    public function getUrl() {
        $state = $this->getState();
        return "https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id={$this->appid}&redirect_uri={$this->callbackUrl}&scope=get_user_info&state={$state}";
    }

    /**
     * 获取用户信息
     * 调用该方法前，自行进 state 的校验
     * @param $code string 同步回调地址中的code
     * @return array
     * @throws \Exception
     */
    public function getAcceccToken($code) {
        // 获取access_token
        $response = file_get_contents("https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id={$this->appid}&client_secret={$this->appkey}&code={$code}&redirect_uri={$this->callbackUrl}");
        // 判断是否有错误
        if (strpos($response, "callback") !== false) {
            $lpos = strpos($response, "(");
            $rpos = strrpos($response, ")");
            $response = substr($response, $lpos + 1, $rpos - $lpos - 1);
            $msg = json_decode($response);
            if (isset($msg->error)) {
                $this->log('获取access_token异常', []);
                throw new \Exception('获取access_token失败');
            }
            $info = array();
            parse_str($response, $info);
            return $info;
        } else {
            $this->log('QQ登录', ['QQ服务连接异常']);
            throw new \Exception('请求错误');
        }
    }

    /**
     * @param $accessToken
     * @return false|mixed|string
     * @throws \Exception
     */
    public function getOpenID($accessToken) {
        // 获取openID
        $response = file_get_contents("https://graph.qq.com/oauth2.0/me?access_token={$accessToken}&unionid=1");
        if (strpos($response, "callback") !== false) {
            $lpos = strpos($response, "(");
            $rpos = strrpos($response, ")");
            $response = substr($response, $lpos + 1, $rpos - $lpos - 1);
        }
        $response = json_decode($response, true);
        if (isset($response['error'])) {
            $this->log('获取openid异常', $response);
            throw new \Exception('accessToken错误');
        }
        $response['access_token'] = $accessToken;
        return $response;
    }

    /**
     * 获取用户信息
     * @param $openid array
     * @return array
     * @throws \Exception
     */
    public function getUserInfo($openid) {
        // 获取用户信息
        $url = "https://graph.qq.com/user/get_user_info?access_token={$openid['access_token']}&oauth_consumer_key={$this->appid}&openid={$openid['openid']}";
        $response = json_decode(file_get_contents($url), true);
        if ($response['ret'] > 0) {
            $this->log('获取userinfo异常', $response);
            throw new \Exception('openid错误');
        }
        return [
            'openid' => $openid['openid'],
            'unionid' => $openid['unionid'],
            'nickname' => mb_substr(filterEmoji($response['nickname']), 0, 20),
            'headimgurl' => $response['figureurl_qq_2']
        ];
    }

    /**
     * 生成唯一随机串防CSRF攻击
     * @return string
     */
    private function getState() {
        return md5(uniqid(rand(), TRUE));
    }

    /**
     * @param string $message
     * @param array $context
     */
    private function log(string $message, array $context) {
        Log::get('qq_login')->error($message, $context);
    }
}
